package fun.codedesign.jdk.math.algorithm.nlp;


import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * TF-IDF算法 <br>
 * <pre>
 *  TF term frequency 词频，在一篇文档中该词所在的词频
 *  IDF inverse document frequency 反文档频率，该词在其他文档中出现的频率
 *  权重 tfi dfij = tfi,j * idfi
 *
 *  tfi,j  词i在在j文档的词频 tfi,j = ni,j/∑knk,j
 *  idfi = log|D|/|{j:ti∈dj}|  总文档数/包含指定词的文档的商取对数
 *  比如 0.5取对数则为0.3
 *
 *  算法用来评估，某个词对于某语料库或者文件集的权重如何，是常用的加权方法
 * <pre/>
 * TF-IDF存在的问题在于某些词的区分性很好，但是却因为文档之间类似导致其权重低，比如
 * 鲜花多少钱？
 * 白百合多少钱？
 * 水仙花多少钱？
 * 苹果多少钱？
 * 以上应该以 多少钱进行切分，但是因为它们在不同的文档当中都有相同的词所以权重低，此时需要将这些相似的文档统一
 * 到一个文档中，然后再进行区分
 *
 *
 * @author zengjian
 * @create 2018-05-30 20:18
 * @since 1.0.0
 */
public class TfIdf {

    /**
     * 统计词频 <br>
     *
     * @param text 已空格分词的文档文本
     * @return
     */
    public static Map<String, Double> countTermFrequency(String text) {
        String[] lines = text.split("\n");
        Map<String, Integer> wordMap = new HashMap<>();
        // 总词数
        int totalCount = 0;
        for (String line : lines) {
            String[] words = line.split(" ");
            totalCount += words.length;
            for (String word : words) {
                if (!wordMap.containsKey(word)) {
                    wordMap.put(word, 1);
                } else {
                    int v = wordMap.get(word);
                    wordMap.put(word, ++v);
                }
            }
        }

        Map<String, Double> tfMap = new HashMap<>();
        for (Map.Entry<String, Integer> wordEntry : wordMap.entrySet()) {
            tfMap.put(wordEntry.getKey(), 1.0 * wordEntry.getValue() / totalCount);
        }
        return tfMap;
    }

    /**
     * 该词的反文档频率，即在一类文档中除了本身的其他文档存在的次数对数 <br>
     *
     * @param results   多个文本的词频集合
     * @param wordTfMap 单个文本词频集合
     * @return
     */
    public static Map<String, Double> countInverseDocumentFrequency(List<Map<String, Double>> results, Map<String, Double> wordTfMap) {
        // 总文本数
        int totalClassNum = results.size();
        Map<String, Integer> containsWordNumMap = new HashMap<>();
        for (String word : wordTfMap.keySet()) {
            int countOtherClassNum = 1;
            for (Map<String, Double> result : results) {
                if (result.containsKey(word)) {
                    countOtherClassNum++;
                }
            }
            containsWordNumMap.put(word, countOtherClassNum);
        }
        Map<String, Double> idfMap = new HashMap<>();
        for (Map.Entry<String, Integer> countEntry : containsWordNumMap.entrySet()) {
            idfMap.put(countEntry.getKey(), Math.log10(totalClassNum-1 / countEntry.getValue() * 1.0));
        }
        return idfMap;
    }

    /**
     * 计算权重值 <br>
     * @param wordTfMap 词频比率
     * @param wordIdfMap 反文档比率
     * @return
     */
    public static Map<String,Double> countTfIdf(Map<String,Double> wordTfMap, Map<String,Double> wordIdfMap){
        Map<String,Double> tfIdFMap  = new HashMap<>();
        for (String s : wordTfMap.keySet()) {
            tfIdFMap.put(s, wordIdfMap.get(s)*wordIdfMap.get(s));
        }
        return tfIdFMap;
    }
}
